/* * XAuth.java * * Copyright (C) 2005-2010 Tommi Laukkanen * http://www.substanceofcode.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.substanceofcode.twitter; import com.substanceofcode.utils.Base64; import com.substanceofcode.utils.HttpUtil; import com.substanceofcode.utils.ResultParser; import com.substanceofcode.utils.StringUtil; import java.io.UnsupportedEncodingException; import java.util.Date; import java.util.Enumeration; import java.util.Random; import java.util.Vector; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.params.KeyParameter; /** * * @author Tommi Laukkanen (tlaukkanen at gmail dot com) */ public class XAuth { private String xauthUsername; private String xauthPassword; private String token; private String tokenSecret; private String verifier; private static final String OAuthVersion = "1.0"; private static final String OAuthParameterPrefix = "oauth_"; private static final String OAuthConsumerKeyKey = "oauth_consumer_key"; private static final String OAuthCallbackKey = "oauth_callback"; private static final String OAuthVersionKey = "oauth_version"; private static final String OAuthSignatureMethodKey = "oauth_signature_method"; private static final String OAuthSignatureKey = "oauth_signature"; private static final String OAuthTimestampKey = "oauth_timestamp"; private static final String OAuthNonceKey = "oauth_nonce"; private static final String OAuthTokenKey = "oauth_token"; private static final String OAuthTokenSecretKey = "oauth_token_secret"; private static final String OAuthVerifier = "oauth_verifier"; private static final String XAuthUsername = "x_auth_username"; private static final String XAuthPassword = "x_auth_password"; private static final String XAuthMode = "x_auth_mode"; private static final String OAUTH_CONSUMER_TOKEN = "xxx"; private static final String OAUTH_CONSUMER_SECRET = "xxx"; private static final String HMACSHA1SignatureType = "HMAC-SHA1"; private String normalizedUrl = ""; private String normalizedRequestParameters = ""; public XAuth(String username, String password) { this.xauthUsername = username; this.xauthPassword = password; } public void setTokenAndSecret(String token, String secret) { this.token = token; this.tokenSecret = secret; } public String xAuthWebRequest( boolean isPost, String url, QueryParameter[] parameters, ResultParser parser) throws Exception { String outUrl = ""; String querystring = ""; String ret = ""; String postData = ""; String method = "GET"; //Setup postData for signing. //Add the postData to the querystring. if (isPost) { method = "POST"; if (parameters!=null && parameters.length > 0) { //Decode the parameters and re-encode using the oAuth UrlEncode method. for(int i=0; i<parameters.length; i++) { QueryParameter q = parameters[i]; if(postData.length()>0) { postData += "&"; } postData += q.getName() + "=" + encode(q.getValue()); } if (url.indexOf("?") > 0) { url += "&"; } else { url += "?"; } url += postData; } } String nonce = this.generateNonce(); String timeStamp = this.generateTimeStamp(); //Generate Signature String sig = this.generateSignature( url, OAUTH_CONSUMER_TOKEN, OAUTH_CONSUMER_SECRET, this.token, this.tokenSecret, this.verifier, this.xauthUsername, this.xauthPassword, method, timeStamp, nonce); outUrl = normalizedUrl; querystring = normalizedRequestParameters; System.out.println("Signature: " + sig); if(querystring.length()>0) { querystring += "&"; } querystring += "oauth_signature=" + encode(sig); //Convert the querystring to postData /*if (isPost) { postData = querystring; querystring = ""; }*/ if (querystring.length() > 0) { outUrl += "?"; } ret = webRequest(method, outUrl + querystring, postData, parser); return ret; } private String webRequest( String method, String url, String postData, ResultParser parser) throws Exception { String result = ""; System.out.println("web request URL: " + url); if (method.equals("POST")) { if(parser!=null) { result = HttpUtil.doPost(url,parser); } else { result = HttpUtil.doPost(url); } } else { if(parser!=null) { result = HttpUtil.doGet(url,parser); } else { result = HttpUtil.doGet(url); } } return result; } private Vector getQueryParameters(String url) { int questionMarkIndex = url.indexOf("?"); if(questionMarkIndex<0) { return new Vector(); } String parameters = url.substring(questionMarkIndex+1); Vector params = new Vector(); String[] para = StringUtil.split(parameters, "&"); for(int i=0; i<para.length; i++) { if(para[i].startsWith(OAuthParameterPrefix)==false) { String[] nameValue = StringUtil.split(para[i], "="); QueryParameter q = new QueryParameter(nameValue[0], nameValue[1]); params.addElement(q); } } return params; } public String generateSignatureBase( String url, String consumerKey, String token, String tokenSecret, String verifier, String xAuthUsername, String xAuthPassword, String httpMethod, String timeStamp, String nonce, String signatureType) { if (token == null) { token = ""; } if (tokenSecret == null) { tokenSecret = ""; } //normalizedUrl = null; //normalizedRequestParameters = null; Vector parameters = getQueryParameters(url); parameters.addElement(new QueryParameter(OAuthVersionKey, OAuthVersion)); parameters.addElement(new QueryParameter(OAuthNonceKey, nonce)); parameters.addElement(new QueryParameter(OAuthTimestampKey, timeStamp)); parameters.addElement(new QueryParameter(OAuthSignatureMethodKey, signatureType)); parameters.addElement(new QueryParameter(OAuthConsumerKeyKey, consumerKey)); if (token!=null && token.length()!=0) { parameters.addElement(new QueryParameter(OAuthTokenKey, token)); } else { if ( xAuthUsername!=null && xAuthUsername.length()!=0) { parameters.addElement(new QueryParameter(XAuthUsername, xAuthUsername)); } if ( xAuthPassword!=null && xAuthPassword.length()!=0) { parameters.addElement(new QueryParameter(XAuthPassword, xAuthPassword)); parameters.addElement(new QueryParameter(XAuthMode, "client_auth")); } } if (verifier!=null && verifier.length()!=0) { parameters.addElement(new QueryParameter(OAuthVerifier, verifier)); } sortParameters( parameters ); normalizedUrl = getSchemeAndHost(url); normalizedUrl += getAbsolutePath(url); System.out.println("Normalized url: " + normalizedUrl); normalizedRequestParameters = normalizeRequestParameters(parameters); System.out.println("Normalized params: " + normalizedRequestParameters); StringBuffer signatureBase = new StringBuffer(); signatureBase.append(httpMethod + "&"); signatureBase.append(encode(normalizedUrl) + "&"); signatureBase.append(encode(normalizedRequestParameters)); String sigBase = signatureBase.toString(); System.out.println("Signature base: " + sigBase); return sigBase; } private static String getSchemeAndHost(String url) { int startIndex = url.indexOf("//")+2; int endIndex = url.indexOf("/", startIndex); return url.substring(0,endIndex); } private static String getAbsolutePath(String url) { int startIndex = url.indexOf("//")+2; int endIndex = url.indexOf("/", startIndex); int questionMark = url.indexOf("?"); if(questionMark>0) { return url.substring(endIndex, questionMark); } else { return url.substring(endIndex); } } private static void sortParameters(Vector items) { boolean unsorted = true; System.out.println("Sorting..."); while(unsorted) { unsorted = false; for(int i=items.size()-1; i>0; i--) { System.out.println("Compare..."); QueryParameter item1 = (QueryParameter)items.elementAt(i); QueryParameter item2 = (QueryParameter)items.elementAt(i-1); if(item1.getName().compareTo(item2.getName())<0) { System.out.println("Change..."); items.setElementAt(item1, i-1); items.setElementAt(item2, i); unsorted = true; } } } } private String generateSignature( String url, String consumerKey, String consumerSecret, String token, String tokenSecret, String verifier, String xAuthUsername, String xAuthPassword, String httpMethod, String timeStamp, String nonce) { String signatureBase = generateSignatureBase( url, consumerKey, token, tokenSecret, verifier, xAuthUsername, xAuthPassword, httpMethod, timeStamp, nonce, HMACSHA1SignatureType); String tokenSec = ""; if(tokenSecret!=null) { tokenSec = tokenSecret; } String key = encode(consumerSecret) + "&" + encode(tokenSec); return getSignature(signatureBase, key); } public String getSignature(String message, String key) { try { HMac m=new HMac(new SHA1Digest()); m.init(new KeyParameter(key.getBytes("UTF-8"))); byte[] bytes=message.getBytes("UTF-8"); m.update(bytes, 0, bytes.length); byte[] mac = new byte[m.getMacSize()]; m.doFinal(mac, 0); String signature = new Base64().encode(mac); return signature; } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); } return null; } protected String normalizeRequestParameters(Vector parameters) { StringBuffer sb = new StringBuffer(); QueryParameter p = null; Enumeration en = parameters.elements(); while(en.hasMoreElements()) { p = (QueryParameter)en.nextElement(); sb.append(p.getName()).append("=").append(p.getValue()); if (en.hasMoreElements()) { sb.append("&"); } } return sb.toString(); } public String generateTimeStamp() { Date d = new Date(); String timestamp = Long.toString(d.getTime()/1000); return timestamp; } public String generateNonce() { Random random = new Random(); String nonce = Long.toString(Math.abs(random.nextLong()), 60000); return nonce; } private String unreservedCharactersPattern = "[a-zA-Z0-9\\-\\._~]"; private String unreservedCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~"; private String encode(String s) { if (s == null || "".equals(s)) { return ""; } StringBuffer sb = new StringBuffer(s.length()*2); for (int i = 0; i < s.length(); i++) { if (unreservedCharacters.indexOf(s.charAt(i)) == -1) { // get byte values of the character // and turn them into percent encoding String t = String.valueOf(s.charAt(i)); sb.append(StringUtil.urlEncode(t)); } else { sb.append(s.charAt(i)); } } return sb.toString(); } }